/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.lemurian.bankdata.bean.base;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.ObjectDeletedException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;

import com.lemurian.bankdata.lite.struts.PartialList;

/**
 * 
 * @author Aditya Lukman Afandi
 *
 */

public class BaseDaoHibernate<T> {

	protected Class domainClass;
	
    @Autowired protected SessionFactory sessionFactory;

    public BaseDaoHibernate() {
    	this.domainClass = (Class)((ParameterizedType) getClass().getGenericSuperclass() ).getActualTypeArguments()[0];
    }
    
    public Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	public Class setDomainClass(Class domainClass) {
		return this.domainClass = domainClass;
	}

	public void delete(T entity){
		try {
			sessionFactory.getCurrentSession().delete(entity);
		} catch(Exception exception){
			sessionFactory.getCurrentSession().getTransaction().rollback();
		}	
	}
	
	// Boolean Version
	public <err extends ErrorMessageDAO> boolean makeTransient(T entity, ErrorMessageDAO err) {
		boolean state = true;
		try {
			sessionFactory.getCurrentSession().delete(entity);
		} catch(ObjectDeletedException ode) {
			state = false;
			sessionFactory.getCurrentSession().getTransaction().rollback();
			ode.printStackTrace();
			err.setErrorMsg(ObjectDeletedException.class.getSimpleName());
		} catch (Exception e) {
			state = false;
			sessionFactory.getCurrentSession().getTransaction().rollback();
			e.printStackTrace();
			err.setErrorMsg(e.getMessage());
		}
		return state;
	}
	
	@SuppressWarnings("unchecked")
	public void delete(int id){
		try {
			T t = (T) sessionFactory.getCurrentSession().get(domainClass, id);
			if (t!=null) sessionFactory.getCurrentSession().delete(t);
		} catch(Exception exception){
			sessionFactory.getCurrentSession().getTransaction().rollback();
		}		
	}	
	
	public int delete(String sql){
		int i = 0;
		try {
			Query query = sessionFactory.getCurrentSession().createQuery(sql);
			i = query.executeUpdate();
		} catch(Exception exception){
			sessionFactory.getCurrentSession().getTransaction().rollback();
		}	
		return i;
	}
	

	@SuppressWarnings("unchecked")
	public List<T> findAll(){
		List<T> list = null;
		try {
			Criteria criteria = sessionFactory.getCurrentSession().createCriteria(domainClass);
			criteria.setCacheable(true);
			list = criteria.list();
		} catch(Exception exception){
			//sessionFactory.getCurrentSession().getTransaction().rollback();
			exception.printStackTrace();
		}
		return list;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findAll(Order order){
		List<T> list = null;
		try {
			Criteria criteria = sessionFactory.getCurrentSession().createCriteria(domainClass);
			criteria.setCacheable(true);
			list = criteria.addOrder(order).list();
		} catch(Exception exception){
			//sessionFactory.getCurrentSession().getTransaction().rollback();
			exception.printStackTrace();
		}
		return list;
	}

	@SuppressWarnings("unchecked")
	public T findById(int id){
		T entity = null;
		try {
			entity = (T) sessionFactory.getCurrentSession().get(domainClass, (Serializable) id);
		} catch(Exception exception){
			sessionFactory.getCurrentSession().getTransaction().rollback();
		}
		return entity;
	}
	
	@SuppressWarnings("unchecked")
	public T load(int id){
		T entity = null;
		try {
			entity = (T)sessionFactory.getCurrentSession().load(domainClass, id);
		} catch(Exception exception){
			sessionFactory.getCurrentSession().getTransaction().rollback();
		}
		return entity;
	}

	public T save(T entity){
		try {
			sessionFactory.getCurrentSession().saveOrUpdate(entity);
		} catch(Exception exception){
			sessionFactory.getCurrentSession().getTransaction().rollback();
		}
		return entity;
	}
	
	public T update(T entity){
		try {
			sessionFactory.getCurrentSession().update(entity);
		} catch(Exception exception){
			sessionFactory.getCurrentSession().getTransaction().rollback();
		}
		return entity;
	}
	
	// Boolean Version
	public <err extends ErrorMessageDAO> boolean makePersistent(T entity, ErrorMessageDAO err) {
		boolean state = true;
		try {
			sessionFactory.getCurrentSession().saveOrUpdate(entity);
		} catch (Exception e) {
			state = false;
			sessionFactory.getCurrentSession().getTransaction().rollback();
			e.printStackTrace();
			err.setErrorMsg(e.getMessage());
		}
		return state;
	}
	
	public boolean makeTransient(T entity){
		boolean state = true;
		try {
			sessionFactory.getCurrentSession().update(entity);
		} catch(Exception exception){
			sessionFactory.getCurrentSession().getTransaction().rollback();
			state = false;
		}
		return state;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Order order, Criterion... criterion){
		List<T> list = null;
		try {
			Criteria criteria = sessionFactory.getCurrentSession().createCriteria(domainClass);
			criteria.setCacheable(true);
			for (Criterion c : criterion) {
				criteria.add(c);
			}
			if (order!=null) criteria.addOrder(order);
			list = criteria.list();
		} catch(Exception exception){
			exception.printStackTrace();
			//sessionFactory.getCurrentSession().getTransaction().rollback();
		}
		return list;
	}

	@SuppressWarnings("unchecked")
	public PartialList<T> findByCriteria(int start, int count, Order order, Criterion... criterion){
		PartialList<T> partialList = null;
		try {
			// total
			Criteria criteria = sessionFactory.getCurrentSession().createCriteria(domainClass);
			
			criteria.setCacheable(true);
			for (Criterion c : criterion) {
				criteria.add(c);
			}
			criteria.setProjection(Projections.rowCount());
			int total = ((Integer) criteria.list().iterator().next()).intValue();
			// partial data
			criteria = sessionFactory.getCurrentSession().createCriteria(domainClass);
			criteria.setCacheable(true);
			for (Criterion c : criterion) {
				criteria.add(c);
			}
			if (order!=null) criteria.addOrder(order);
			criteria.setFirstResult(start);
			criteria.setMaxResults(count);
			
			partialList = new PartialList<T>(criteria.list(), total);
		} catch(Exception exception){
			exception.printStackTrace();
			//sessionFactory.getCurrentSession().getTransaction().rollback();
		}
		return partialList;
	}
	
	@SuppressWarnings("unchecked")
	public T findByCriteria(Criterion... criterion){
		T t = null;
		try {
			Criteria criteria = sessionFactory.getCurrentSession().createCriteria(domainClass);
			criteria.setCacheable(true);
			for (Criterion c : criterion) {
				criteria.add(c);
			}
			t = (T)criteria.setMaxResults(1).uniqueResult();
		} catch(Exception exception){
			exception.printStackTrace();
			//sessionFactory.getCurrentSession().getTransaction().rollback();
		}
		return t;
	}
	
	@SuppressWarnings("unchecked")
    public List<T> nativeQuery(String query) {
    	return sessionFactory.getCurrentSession().createQuery(query).list();
    }
	
	@SuppressWarnings("unchecked")
	public Object getByUniqueField(Class type, Object pk, String fieldName) {
    	try {
    		Query query = sessionFactory.getCurrentSession().createQuery("SELECT a FROM " + type.getName() + " a WHERE a." + fieldName + "=:pk");
	        query.setParameter("pk", pk);
            return query.uniqueResult();
        } catch (Exception e) {
            return null;
        }
    }
}
